package com.sistemagestion.DAOsqlite;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.sistemagestion.DAO.IGenericDao;
import com.sistemagestion.Utils.NotMapping;
import com.sistemagestion.Utils.PrimaryKey;

public class RepositorySqliteDao<T, ID extends Serializable> implements
		IGenericDao<T, Serializable> {

	private Class<T> classRepository;
	private String TableName = "";

	@SuppressWarnings("unchecked")
	public RepositorySqliteDao() {
		ParameterizedType thisType = (ParameterizedType) getClass()
				.getGenericSuperclass();
		classRepository = (Class<T>) thisType.getActualTypeArguments()[0];
		TableName = classRepository.getSimpleName();
	}

	public long saveObject(T entity) {
		SqliteDaoFactory daoFactory = new SqliteDaoFactory();
		SQLiteDatabase database = daoFactory.open();
		ContentValues values = new ContentValues();
		Field[] fields = classRepository.getDeclaredFields();
		int id = -1;
		for (Field field : fields) {
			if (!(field.isAnnotationPresent(NotMapping.class))) {
				String getterName = "get" + field.getName();
				try {
					Method method = entity.getClass().getMethod(getterName);
					Object valueObject = method.invoke(entity, (Object[]) null);
					if (field.getName().startsWith("Id")
							&& field.isAnnotationPresent(PrimaryKey.class))
						id = Integer.parseInt(valueObject.toString());
					else
						values.put(field.getName().toString(),
								valueObject != null ? valueObject.toString()
										: "");

				} catch (Exception e) {
					System.out.println(e.toString());
				}
			}
		}
		try {
			if (getById(id) == null) {
				id = getNewId();
				values.put(getColumnId(), id);
				database.insert(TableName, null, values);
			} else{
				database.update(TableName, values,
						getColumnId().concat(" = ?"),
						new String[] { Integer.toString(id) });
			}
			return (long)id;
		}
		
		catch (Exception e) {
			System.out.println(e.toString());
			return 0;
		}
	}

	public List<T> getAll() {
		SqliteDaoFactory daoFactory = new SqliteDaoFactory();
		SQLiteDatabase database = daoFactory.open();
		Cursor cur = database.query(TableName, null, null, null, null, null,
				null);
		List<T> lista = new ArrayList<T>();
		lista = CursorToArrayList(cur);
		daoFactory.close();
		cur.close();
		return lista;
	}

	public List<T> getByQuery(List<String> prop, List<String> values,
			List<String> ops) {
		String query = "";
		int index = 0;
		SqliteDaoFactory daoFactory = new SqliteDaoFactory();
		SQLiteDatabase database = daoFactory.open();
		Cursor cur = null;
		for (String _prop : prop) {
			query = query.concat(_prop.concat(ops.get(index).concat("?")));
			if (index <= prop.size())
				query = query.concat(" and ");
		}
		return null;
	}
	
	public int ExecuteSqlScalarQuery(String query) {
		int value = 0;
		SqliteDaoFactory daoFactory = new SqliteDaoFactory();
		SQLiteDatabase database = daoFactory.open();
		Cursor cur = null;
		cur = database.rawQuery(query, null);
		if (cur.moveToFirst()) {
			value = cur.getInt(0);
			cur.close();
			return value;
		}
		return 0;
	}

	@Override
	public T getById(Integer id) throws Exception {
		// TODO Auto-generated method stub
		SqliteDaoFactory daoFactory = new SqliteDaoFactory();
		SQLiteDatabase database = daoFactory.open();
		Cursor cur = null;
		cur = database.query(TableName, null,
				getColumnId().concat(" = ".concat(Integer.toString(id))), null,
				null, null, null);
		if (cur.moveToFirst()) {
			List<T> lista = new ArrayList<T>();
			lista = CursorToArrayList(cur);
			daoFactory.close();
			cur.close();
			return lista.get(0);
		} else{
			daoFactory.close();
			cur.close();
			return null;
		}

	}

	@Override
	public void update(T entity) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void delete(T entity) throws Exception {
		// TODO Auto-generated method stub
		SqliteDaoFactory daoFactory = new SqliteDaoFactory();
		SQLiteDatabase database = daoFactory.open();
		Method method = entity.getClass()
				.getMethod("get".concat(getColumnId()));
		Object valueObject = method.invoke(entity, (Object[]) null);
		database.delete(TableName, getColumnId().concat(" = ? "),
				new String[] { valueObject.toString() });
	}


	public int getNewId() {
		SqliteDaoFactory daoFactory = new SqliteDaoFactory();
		SQLiteDatabase database = daoFactory.open();
		Cursor cur = null;
		int id = 0;
		cur = database.rawQuery("SELECT MAX(" + getColumnId() + ") FROM "
				+ TableName, null);
		if (cur.moveToFirst()) {
			id = cur.getInt(0) + 1;
			cur.close();
			return id;
		}
		cur.close();
		return 0;
	}

	private List<T> CursorToArrayList(Cursor cur) {
		Class clazz = null;
		List<T> lista = new ArrayList<T>();
		Object obj = new Object();
		try {

			clazz = Class.forName(classRepository.getName());
			obj = clazz.newInstance();
		} catch (Exception e) {

		}
		if (cur.moveToFirst()) {
			Field[] fields = classRepository.getDeclaredFields();
			do {
				try {
					obj = clazz.newInstance();
				} catch (Exception ex) {
				}

				for (Field field : fields) {
					if (!(field.isAnnotationPresent(NotMapping.class))) {
						String setterName = "set" + field.getName();
						try {

							int index = cur.getColumnIndex(field.getName());
							if (field.getType().isAssignableFrom(String.class)) {
								Method method = obj.getClass()
										.getDeclaredMethod(setterName,
												String.class);
								method.invoke(obj,
										new Object[] { cur.getString(index) });
							}
							if (field.getType().isAssignableFrom(Integer.TYPE)) {
								Method method = obj.getClass()
										.getDeclaredMethod(setterName,
												Integer.TYPE);
								method.invoke(obj,
										new Object[] { cur.getInt(index) });
							}
							if (field.getType().isAssignableFrom(Integer.class)) {
								Method method = obj.getClass()
										.getDeclaredMethod(setterName,
												Integer.class);
								method.invoke(obj,
										new Object[] { cur.getInt(index) });
							}
							if (field.getType().isAssignableFrom(Float.class)) {
								Method method = obj.getClass()
										.getDeclaredMethod(setterName,
												Float.class);
								method.invoke(obj,
										new Object[] { cur.getFloat(index) });	
							}
							if (field.getType().isAssignableFrom(Double.class)) {
								Method method = obj.getClass()
										.getDeclaredMethod(setterName,
												Double.class);
								method.invoke(obj,
										new Object[] { cur.getDouble(index) });
							}
							

						} catch (Exception e) {
							Log.e("", e.toString());
						}
					}
				}

				lista.add((T) obj);
			}

			while (cur.moveToNext());
		}
		cur.close();
		return lista;
	}

	private String getColumnId() {

		Field[] fields = classRepository.getDeclaredFields();
		for (Field field : fields) {
			if (field.isAnnotationPresent(PrimaryKey.class))
				return field.getName().toString();
		}
		return "";
	}
}
